#include "circularbuffer.h"

void CircularBuffer::Init(int64 buffer_size) {
  CHECK(buffer_ == NULL);
  buffer_size_ = buffer_size;
  read_ptr_ = write_ptr_ = buffer_ = new char[buffer_size_];
  buffer_end_ = buffer_ + buffer_size_;
}
void CircularBuffer::ReadOnePart(char **target, int64 bytes) {
  MemCopyAdvance(target, &read_ptr_, bytes);
  if (read_ptr_ == buffer_end_) read_ptr_ = buffer_;
}
void CircularBuffer::WriteOnePart(char **source, int64 bytes) {
  MemCopyAdvance(&write_ptr_, source, bytes);
  if (write_ptr_ == buffer_end_) write_ptr_ = buffer_;
}
void CircularBuffer::Read(char ** target, int64 max_bytes) {
  int64 bytes = min(BytesReadable(), max_bytes);
  int64 first_bytes = min(bytes, (int64) (buffer_end_ - read_ptr_));
  ReadOnePart(target, first_bytes);
  bytes -= first_bytes;
  if (bytes) ReadOnePart(target, bytes);
}
void CircularBuffer::Write(char *source, int64 bytes) {
  CHECK(bytes < buffer_size_);
  while (BytesWritable() < bytes);
  int64 first_bytes = min(bytes, (int64)(buffer_end_ - write_ptr_));    
  WriteOnePart(&source, first_bytes);
  bytes -= first_bytes;
  if (bytes) WriteOnePart(&source, bytes);
}
int64 CircularBuffer::BytesReadable() {
  return ( (write_ptr_ - read_ptr_) + buffer_size_ )  % buffer_size_;
}
int64 CircularBuffer::BytesWritable() {
  return buffer_size_ - 1 - BytesReadable();
}

void * CircularBufferWriteFoo(void *ds) {
  CircularBuffer * stream = (CircularBuffer *)ds;
  char * foo = (char *)"foo";
  while(1) stream->Write(foo, 3);
}

void TestCircularBuffer() {
  CircularBuffer stream;
  stream.Init(16);
  string cmd, s;
  int n;
  char buf[1000];
  while(1) {
    stream.Display();
    cin >> cmd;
    if (cmd == "w") {
      cin >> s;
      stream.Write((char *)s.c_str(), s.length());      
    }
    if (cmd == "r") {
      cin >> n;
      char * target = buf;
      stream.Read(&target, n);
      *target = '\0';
      cout << buf << endl;
    }
    if (cmd == "l") {
      pthread_t * thread = new pthread_t;
      pthread_create(thread, NULL, CircularBufferWriteFoo, &stream);
    }
  }
}

//int main() {  TestCircularBuffer(); }
